//////////////////////////////////////////////////////////////////////////

#ifndef _CONNECTION_H_
#include "connection.h"
#endif

//////////////////////////////////////////////////////////////////////////

#ifndef _CONNECTIONIMPL_H_
#include "connectionimpl.h"
#endif

#include <cassert>

//////////////////////////////////////////////////////////////////////////


Connection::Connection( Mode _workingMode )
{
    switch (_workingMode)
    {
    case CLIENT:
        m_Impl = ConnectionImpl::GetClientConnectionImpl();
        break;

    case SERVER:
        m_Impl = ConnectionImpl::GetServerConnectionImpl();
        break;

    default:
        assert(0);
        break;
    }


    AddEventToObserve( Observer::NETWORK_ACCEPTED_NEW_CONNECTION );
    AddEventToObserve( Observer::NETWORK_DISCONNECT );
    AddEventToObserve( Observer::NETWORK_RECEIVE_COMPLETE_TCP );
    AddEventToObserve( Observer::NETWORK_RECEIVE_COMPLETE_UDP );
    AddEventToObserve( Observer::NETWORK_SEND_COMPLETE_TCP );
    AddEventToObserve( Observer::NETWORK_SEND_COMPLETE_UDP );

    m_Impl->AddObserver(this);

    // Setup connection ID
    static size_t globalNumberOfConnections = 0;
    m_ID = globalNumberOfConnections;
    ++globalNumberOfConnections;
}


//////////////////////////////////////////////////////////////////////////


Connection::~Connection()
{
    if (m_Impl)
        delete m_Impl;
}


//////////////////////////////////////////////////////////////////////////


void Connection::SendTCP( const NetworkPacket& _data )
{
    m_TCPQueueMutex.Lock();
    bool sendNow = m_TCPQueue.empty();
    m_TCPQueue.push(_data);
    m_TCPQueueMutex.Unlock();

    if (sendNow)
        StartSendTCP();
}


//////////////////////////////////////////////////////////////////////////


void Connection::SendUDP( const NetworkPacket& _data )
{
    m_UDPQueueMutex.Lock();
    bool sendNow = m_UDPQueue.empty();
    m_UDPQueue.push(_data);
    m_UDPQueueMutex.Unlock();

    if (sendNow)
        StartSendUDP();
}


//////////////////////////////////////////////////////////////////////////


const NetworkPacket& Connection::GetRecvDataTCP() const
{
    return m_Impl->GetRecvDataTCP();
}


//////////////////////////////////////////////////////////////////////////


const NetworkPacket& Connection::GetRecvDataUDP() const
{
    return m_Impl->GetRecvDataUDP();
}


//////////////////////////////////////////////////////////////////////////


void Connection::Connect( const char* _address)
{
    m_Impl->Connect(_address);
}


//////////////////////////////////////////////////////////////////////////


void Connection::Accept()
{
    m_Impl->Accept();
}


//////////////////////////////////////////////////////////////////////////


void Connection::Close()
{
    m_Impl->Close();
}


//////////////////////////////////////////////////////////////////////////


void Connection::OnEvent( Event _event, Observable* _observable )
{
    switch (_event)
    {
    case NETWORK_SEND_COMPLETE_TCP:
        {
            HandleSendTCP();
            break;
        }
    case NETWORK_SEND_COMPLETE_UDP:
        {
            HandleSendUDP();
            break;
        }

    default:
        // Redirect
        NotifyObservers(_event);
        break;
    }
}


//////////////////////////////////////////////////////////////////////////


bool Connection::IsConnected() const
{
    return m_Impl->IsConnected();
}


//////////////////////////////////////////////////////////////////////////


size_t Connection::GetID() const
{
    return m_ID;
}


//////////////////////////////////////////////////////////////////////////


void Connection::HandleSendTCP()
{
    m_TCPQueueMutex.Lock();
    bool continueSend = !m_TCPQueue.empty();
    m_TCPQueueMutex.Unlock();

    if (continueSend)
        StartSendTCP();
}


//////////////////////////////////////////////////////////////////////////


void Connection::HandleSendUDP()
{
    m_UDPQueueMutex.Lock();
    bool continueSend = !m_UDPQueue.empty();
    m_UDPQueueMutex.Unlock();

    if (continueSend)
        StartSendUDP();
}


//////////////////////////////////////////////////////////////////////////


void Connection::StartSendTCP()
{
    m_TCPQueueMutex.Lock();
    NetworkPacket packet = m_TCPQueue.front();
    m_TCPQueue.pop();
    m_TCPQueueMutex.Unlock();

    m_Impl->SendTCP(packet);
}


//////////////////////////////////////////////////////////////////////////


void Connection::StartSendUDP()
{
    m_UDPQueueMutex.Lock();
    NetworkPacket packet = m_UDPQueue.front();
    m_UDPQueue.pop();
    m_UDPQueueMutex.Unlock();

    m_Impl->SendUDP(packet);
}


//////////////////////////////////////////////////////////////////////////
